package com.xiaoyu.base.net.token;

import android.os.ConditionVariable;

import com.xiaoyu.net.config.Constant;
import com.xiaoyu.net.request.RequestWithJsonDataReturn;
import com.xiaoyu.base.data.UserData;
import in.srain.cube.util.CheckUtil;

import java.util.concurrent.atomic.AtomicBoolean;

import in.srain.cube.request.JsonData;
import in.srain.cube.request.RequestData;
import in.srain.cube.request.RequestDefaultHandler;
import in.srain.cube.util.CLog;
import okhttp3.Request;

public class TokenRefresher {

    private static final String TAG = "TokenRefresher";
    private static final long REFRESH_WAIT_TIMEOUT = Constant.TIMEOUT;
    private static final AtomicBoolean REFRESHING = new AtomicBoolean(false);
    private static final ConditionVariable CONDITION = new ConditionVariable(true);
    private static String sRenewTokenApi;

    public static void setRefreshTokenApi(String renewTokenApi) {
        sRenewTokenApi = renewTokenApi;
    }

    public static void refresh() {
        CheckUtil.checkNotMain();
        if (REFRESHING.compareAndSet(false, true)) {
            CLog.i(TAG, "start refreshing the token");
            CONDITION.close();
            postRenewAccessToken();
            CONDITION.open();
            REFRESHING.set(false);
        } else {
            waitUntilRefreshed();
        }
    }

    private static void waitUntilRefreshed() {
        CLog.i(TAG, "wait until token refreshed");
        if (CONDITION.block(REFRESH_WAIT_TIMEOUT)) {
            CLog.i(TAG, "another thread has refreshed the token");
        } else {
            CLog.e(TAG, "condition block timeout");
        }
    }

    private static void postRenewAccessToken() {
        CLog.i(TAG, "postRenewAccessToken, current token info: %s", UserData.getInstance().getTokenInfo());
        final RequestWithJsonDataReturn<JsonData> request = new RequestWithJsonDataReturn<>();
        final RequestData requestData = request.getRequestData();
        requestData.setRequestUrl(sRenewTokenApi);
        requestData.addPostData("refreshToken", UserData.getInstance().getTokenInfo().getRefreshToken());
        requestData.addPostData("accessToken", UserData.getInstance().getTokenInfo().getAccessToken());

        request.setRequestHandler(new RequestDefaultHandler<JsonData, JsonData>() {
            @Override
            public JsonData processOriginData(JsonData originData) {
                final JsonData data = originData.optJson("data");
                UserData.getInstance().updateToken(data.optJson("token"));
                return originData;
            }
        });

        try {
            request.requestSync();
            CLog.i(TAG, "renew token success, current token info %s", UserData.getInstance().getTokenInfo());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            CLog.e(TAG, "renew token fail %s", throwable.getMessage());
        }
    }

    public static void refreshIfNecessary(Request request) {
        if (isRenewTokenRequest(request)) {
            return;
        }
        if (!UserData.getInstance().shouldRenewAccessToken()) {
            return;
        }
        refresh();
    }

    public static void waitUntilRefreshedIfNecessary(Request request) {
        if (isRenewTokenRequest(request)) {
            return;
        }
        if (!REFRESHING.get()) {
            return;
        }
        waitUntilRefreshed();
    }

    // Hack
    private static boolean isRenewTokenRequest(Request request) {
        return request.url().toString().contains(sRenewTokenApi);
    }
}
